#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
甘蔗农机智能管控平台 - Windows服务器
整合：双摄像头 + 激光雷达 + GPS + PLC
"""
from flask import Flask, render_template, Response, jsonify, request, send_file
from flask_socketio import SocketIO, emit
import socket
import threading
import json
import time
import logging
from datetime import datetime
from database import db
from stats_calculator import calculator
from report_generator import report_gen

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

app = Flask(__name__)
app.config['SECRET_KEY'] = 'gzjy_platform_2024'
socketio = SocketIO(app, cors_allowed_origins="*")

# 全局数据
current_robot = 'GZJY001'
robots = {}
robot_sockets = {}
robot_last_heartbeat = {}  # 记录每个机器人的最后心跳时间

# 心跳超时设置（秒）
HEARTBEAT_TIMEOUT = 15  # 15秒未收到心跳视为离线

def get_robot(robot_id):
    """获取或创建机器人数据"""
    if robot_id not in robots:
        robots[robot_id] = {
            'id': robot_id,
            'connected': False,
            'last_seen': None,
            'lidar': {'points': [], 'count': 0},
            'gps': {'lat': 0, 'lng': 0, 'satellites': 0, 'fix': 'No Fix'},
            'plc': {'sensors': {}, 'connected': False}
        }
    return robots[robot_id]

def update_robot_heartbeat(robot_id):
    """更新机器人心跳时间"""
    robot_last_heartbeat[robot_id] = time.time()
    robot = get_robot(robot_id)
    robot['connected'] = True
    robot['last_seen'] = datetime.now().isoformat()
    logger.debug(f"[心跳] {robot_id} 更新")

def check_robot_timeout():
    """检查机器人超时（后台线程定期执行）"""
    while True:
        try:
            current_time = time.time()
            for robot_id in list(robot_last_heartbeat.keys()):
                last_heartbeat = robot_last_heartbeat.get(robot_id, 0)
                if current_time - last_heartbeat > HEARTBEAT_TIMEOUT:
                    # 超时，标记为离线
                    robot = get_robot(robot_id)
                    if robot['connected']:
                        robot['connected'] = False
                        logger.warning(f"[超时] {robot_id} 离线（超过{HEARTBEAT_TIMEOUT}秒未收到心跳）")
                        
                        # 清理socket
                        if robot_id in robot_sockets:
                            try:
                                robot_sockets[robot_id].close()
                            except:
                                pass
                            del robot_sockets[robot_id]
                        
                        # 通知前端
                        socketio.emit('robot_status_changed', {
                            'robot_id': robot_id,
                            'connected': False,
                            'message': '机器人离线'
                        })
            
            time.sleep(5)  # 每5秒检查一次
        except Exception as e:
            logger.error(f"[超时检查] 错误: {e}")

def send_to_robot(robot_id, command):
    """发送命令到机器人"""
    logger.info(f"[DEBUG] 尝试发送到{robot_id}, 当前sockets: {list(robot_sockets.keys())}")
    
    sock = robot_sockets.get(robot_id)
    if not sock:
        logger.warning(f"[DEBUG] robot_id={robot_id}不在sockets中！")
        return False
    
    try:
        msg = json.dumps(command) + '\n'
        logger.info(f"[DEBUG] 发送消息: {msg[:100]}")
        sock.send(msg.encode())
        logger.info(f"[命令->RK] {robot_id}: {command['type']}")
        return True
    except Exception as e:
        logger.error(f"[DEBUG] 发送失败: {e}")
        if robot_id in robot_sockets:
            del robot_sockets[robot_id]
        return False

# ========== TCP接收器 ==========

def start_control_receiver():
    """端口9000 - 状态+命令通道"""
    def receiver():
        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        server.bind(('0.0.0.0', 9000))
        server.listen(5)
        logger.info("[9000] 命令通道启动")
        
        while True:
            try:
                client, addr = server.accept()
                logger.info(f"[9000] 连接: {addr}")
                
                def handle():
                    robot_id = None
                    buffer = ""
                    
                    while True:
                        try:
                            client.settimeout(0.5)
                            data = client.recv(4096)
                            if not data:
                                break
                            
                            buffer += data.decode('utf-8')
                            while '\n' in buffer:
                                line, buffer = buffer.split('\n', 1)
                                if line.strip():
                                    msg = json.loads(line)
                                    msg_type = msg.get('type')
                                    
                                    if msg_type == 'video':
                                        robot_id = msg.get('robot_id', 'UNKNOWN')
                                        logger.info(f"[DEBUG] 保存socket: robot_id={robot_id}")
                                        robot_sockets[robot_id] = client
                                        logger.info(f"[DEBUG] 当前sockets: {list(robot_sockets.keys())}")
                                        update_robot_heartbeat(robot_id)
                                        
                                        # 通知前端机器人上线
                                        socketio.emit('robot_status_changed', {
                                            'robot_id': robot_id,
                                            'connected': True,
                                            'message': '机器人上线'
                                        })
                                    
                                    elif msg_type == 'heartbeat':
                                        # 心跳消息
                                        robot_id = msg.get('robot_id')
                                        if robot_id:
                                            update_robot_heartbeat(robot_id)
                        except socket.timeout:
                            continue
                        except:
                            break
                    
                    if robot_id and robot_id in robot_sockets:
                        del robot_sockets[robot_id]
                        get_robot(robot_id)['connected'] = False
                    client.close()
                    logger.info(f"[9000] 断开: {addr}")
                
                threading.Thread(target=handle, daemon=True).start()
            except:
                pass
    
    threading.Thread(target=receiver, daemon=True).start()

def start_lidar_receiver():
    """端口9001 - 激光雷达数据"""
    def receiver():
        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        server.bind(('0.0.0.0', 9001))
        server.listen(5)
        logger.info("[9001] 雷达数据启动")
        
        while True:
            try:
                client, addr = server.accept()
                
                def handle():
                    buffer = ""
                    while True:
                        try:
                            data = client.recv(4096)
                            if not data:
                                break
                            buffer += data.decode('utf-8')
                            while '\n' in buffer:
                                line, buffer = buffer.split('\n', 1)
                                if line.strip():
                                    msg = json.loads(line)
                                    if msg.get('type') == 'lidar_parsed':
                                        robot_id = msg.get('robot_id')
                                        robot = get_robot(robot_id)
                                        robot['lidar']['points'] = msg.get('points', [])
                                        robot['lidar']['count'] = msg.get('point_count', 0)
                        except:
                            break
                    client.close()
                
                threading.Thread(target=handle, daemon=True).start()
            except:
                pass
    
    threading.Thread(target=receiver, daemon=True).start()

def start_gps_receiver():
    """端口9002 - GPS数据"""
    def receiver():
        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        server.bind(('0.0.0.0', 9002))
        server.listen(5)
        logger.info("[9002] GPS数据启动")
        
        while True:
            try:
                client, addr = server.accept()
                
                def handle():
                    buffer = ""
                    gps_log_counter = 0
                    while True:
                        try:
                            data = client.recv(4096)
                            if not data:
                                break
                            buffer += data.decode('utf-8')
                            while '\n' in buffer:
                                line, buffer = buffer.split('\n', 1)
                                if line.strip():
                                    msg = json.loads(line)
                                    if msg.get('type') == 'gps_data':
                                        robot_id = msg.get('robot_id')
                                        robot = get_robot(robot_id)
                                        gps = msg.get('data', {})
                                        robot['gps']['lat'] = gps.get('latitude', 0)
                                        robot['gps']['lng'] = gps.get('longitude', 0)
                                        robot['gps']['satellites'] = gps.get('satellites', 0)
                                        robot['gps']['fix'] = gps.get('fix_type', 'No Fix')
                                        
                                        # 如果有活跃会话，记录GPS轨迹（每5个点记录一次）
                                        session_id = robot.get('active_session')
                                        if session_id:
                                            gps_log_counter += 1
                                            if gps_log_counter % 5 == 0:
                                                db.log_gps_track(
                                                    robot_id,
                                                    gps.get('latitude'),
                                                    gps.get('longitude'),
                                                    session_id,
                                                    gps.get('altitude'),
                                                    gps.get('speed'),
                                                    gps.get('heading'),
                                                    gps.get('satellites')
                                                )
                        except:
                            break
                    client.close()
                
                threading.Thread(target=handle, daemon=True).start()
            except:
                pass
    
    threading.Thread(target=receiver, daemon=True).start()

def start_plc_receiver():
    """端口9003 - PLC传感器数据"""
    def receiver():
        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        server.bind(('0.0.0.0', 9003))
        server.listen(5)
        logger.info("[9003] PLC数据启动")
        
        while True:
            try:
                client, addr = server.accept()
                logger.info(f"[PLC] 连接: {addr}")
                
                def handle():
                    buffer = ""
                    while True:
                        try:
                            data = client.recv(4096)
                            if not data:
                                break
                            buffer += data.decode('utf-8')
                            while '\n' in buffer:
                                line, buffer = buffer.split('\n', 1)
                                if line.strip():
                                    msg = json.loads(line)
                                    if msg.get('type') == 'sensor_data':
                                        robot_id = msg.get('robot_id')
                                        robot = get_robot(robot_id)
                                        robot['plc']['sensors'] = msg.get('sensors', {})
                                        robot['plc']['connected'] = True
                                        socketio.emit('sensor_data', {
                                            'robot_id': robot_id,
                                            'sensors': robot['plc']['sensors']
                                        })
                        except:
                            break
                    client.close()
                
                threading.Thread(target=handle, daemon=True).start()
            except:
                pass
    
    threading.Thread(target=receiver, daemon=True).start()

# ========== Web路由 ==========

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/video')
def video1():
    def generate():
        while True:
            try:
                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                sock.settimeout(10)
                sock.connect(('192.168.250.110', 8080))  # WiFi局域网IP
                while True:
                    data = sock.recv(16384)
                    if not data:
                        break
                    yield data
                sock.close()
                break
            except:
                time.sleep(2)
    return Response(generate(), mimetype='multipart/x-mixed-replace; boundary=frame')

@app.route('/video2')
def video2():
    def generate():
        while True:
            try:
                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                sock.settimeout(10)
                sock.connect(('192.168.250.110', 8081))  # WiFi局域网IP
                while True:
                    data = sock.recv(16384)
                    if not data:
                        break
                    yield data
                sock.close()
                break
            except:
                time.sleep(2)
    return Response(generate(), mimetype='multipart/x-mixed-replace; boundary=frame')

@app.route('/api/lidar')
def api_lidar():
    robot = get_robot(current_robot)
    return jsonify({
        'points': robot['lidar']['points'],
        'point_count': robot['lidar']['count'],
        'status': 'real_data' if robot['lidar']['count'] > 0 else 'disconnected'
    })

@app.route('/api/gps')
def api_gps():
    robot = get_robot(current_robot)
    return jsonify({
        'connected': robot['gps']['lat'] != 0,
        'latitude': robot['gps']['lat'],
        'longitude': robot['gps']['lng'],
        'satellites': robot['gps']['satellites'],
        'fix_type': robot['gps']['fix']
    })

@app.route('/api/robots')
def api_robots():
    """获取所有机器人状态（带详细信息）"""
    robot_list = []
    current_time = time.time()
    
    for rid, r in robots.items():
        last_heartbeat = robot_last_heartbeat.get(rid, 0)
        seconds_since_heartbeat = int(current_time - last_heartbeat) if last_heartbeat > 0 else None
        
        robot_list.append({
            'robot_id': rid,
            'connected': r['connected'],
            'last_seen': r.get('last_seen'),
            'seconds_since_heartbeat': seconds_since_heartbeat,
            'has_socket': rid in robot_sockets,
            'gps_fix': r['gps']['fix'],
            'gps_satellites': r['gps']['satellites'],
            'lidar_points': r['lidar']['count'],
            'plc_connected': r['plc'].get('connected', False)
        })
    
    return jsonify({
        'robots': robot_list, 
        'current': current_robot,
        'heartbeat_timeout': HEARTBEAT_TIMEOUT
    })

@app.route('/api/robot/<robot_id>/status')
def api_robot_status(robot_id):
    """获取单个机器人详细状态"""
    if robot_id not in robots:
        return jsonify({'error': '机器人不存在'}), 404
    
    robot = robots[robot_id]
    last_heartbeat = robot_last_heartbeat.get(robot_id, 0)
    current_time = time.time()
    
    return jsonify({
        'robot_id': robot_id,
        'connected': robot['connected'],
        'last_seen': robot.get('last_seen'),
        'seconds_since_heartbeat': int(current_time - last_heartbeat) if last_heartbeat > 0 else None,
        'has_socket': robot_id in robot_sockets,
        'gps': robot['gps'],
        'lidar': {
            'point_count': robot['lidar']['count'],
            'status': 'active' if robot['lidar']['count'] > 0 else 'inactive'
        },
        'plc': robot['plc']
    })

# ========== GIS API接口 ==========

@app.route('/api/gis/maps')
def api_gis_maps():
    """获取地图列表"""
    try:
        with open('maps/map_list.json', 'r', encoding='utf-8') as f:
            data = json.load(f)
        return jsonify(data)
    except Exception as e:
        logger.error(f"[GIS] 读取地图列表失败: {e}")
        return jsonify({'maps': []})

@app.route('/api/gis/map/<map_id>/paths')
def api_gis_map_paths(map_id):
    """获取指定地图的路径"""
    try:
        filename = f'maps/{map_id}_paths.json'
        with open(filename, 'r', encoding='utf-8') as f:
            data = json.load(f)
        return jsonify(data)
    except Exception as e:
        logger.error(f"[GIS] 读取地图路径失败: {e}")
        return jsonify({'map_id': map_id, 'paths': []})

@app.route('/api/gis/data')
def api_gis_data():
    """获取GIS数据（路径和地块）"""
    try:
        with open('gis_data.json', 'r', encoding='utf-8') as f:
            data = json.load(f)
        return jsonify(data)
    except:
        return jsonify({'paths': [], 'fields': [], 'settings': {}})

@app.route('/api/gis/paths')
def api_gis_paths():
    """获取所有路径"""
    try:
        with open('gis_data.json', 'r', encoding='utf-8') as f:
            data = json.load(f)
        return jsonify({'paths': data.get('paths', [])})
    except:
        return jsonify({'paths': []})

@app.route('/api/gis/fields')
def api_gis_fields():
    """获取所有地块"""
    try:
        with open('maps/fields.json', 'r', encoding='utf-8') as f:
            data = json.load(f)
        return jsonify(data)
    except Exception as e:
        logger.error(f"[GIS] 读取地块失败: {e}")
        return jsonify({'fields': []})

@app.route('/api/gis/field/<field_id>')
def api_gis_field(field_id):
    """获取单个地块详情"""
    try:
        with open('maps/fields.json', 'r', encoding='utf-8') as f:
            data = json.load(f)
        field = next((f for f in data['fields'] if f['id'] == field_id), None)
        if field:
            return jsonify(field)
        return jsonify({'error': 'Field not found'}), 404
    except Exception as e:
        logger.error(f"[GIS] 读取地块失败: {e}")
        return jsonify({'error': str(e)}), 500

@app.route('/api/gis/field/<field_id>/update_progress', methods=['POST'])
def api_update_field_progress(field_id):
    """更新地块作业进度"""
    try:
        from flask import request
        progress = request.json.get('progress', 0)
        
        with open('maps/fields.json', 'r', encoding='utf-8') as f:
            data = json.load(f)
        
        for field in data['fields']:
            if field['id'] == field_id:
                field['progress'] = progress
                field['last_work'] = time.strftime('%Y-%m-%d')
                if progress >= 100:
                    field['status'] = '已完成'
                elif progress > 0:
                    field['status'] = '进行中'
                break
        
        with open('maps/fields.json', 'w', encoding='utf-8') as f:
            json.dump(data, f, ensure_ascii=False, indent=2)
        
        logger.info(f"[GIS] 更新地块进度: {field_id} -> {progress}%")
        return jsonify({'success': True})
    except Exception as e:
        logger.error(f"[GIS] 更新进度失败: {e}")
        return jsonify({'success': False, 'error': str(e)})

@app.route('/api/gis/field/<field_id>', methods=['DELETE'])
def api_delete_field(field_id):
    """删除地块"""
    try:
        with open('maps/fields.json', 'r', encoding='utf-8') as f:
            data = json.load(f)
        
        data['fields'] = [f for f in data['fields'] if f['id'] != field_id]
        
        with open('maps/fields.json', 'w', encoding='utf-8') as f:
            json.dump(data, f, ensure_ascii=False, indent=2)
        
        logger.info(f"[GIS] 删除地块: {field_id}")
        return jsonify({'success': True})
    except Exception as e:
        logger.error(f"[GIS] 删除地块失败: {e}")
        return jsonify({'success': False, 'error': str(e)})

@app.route('/api/gis/create_field', methods=['POST'])
def api_create_field():
    """新建地块"""
    try:
        from flask import request
        field_data = request.json
        
        with open('maps/fields.json', 'r', encoding='utf-8') as f:
            data = json.load(f)
        
        # 生成新ID
        existing_ids = [f['id'] for f in data['fields']]
        field_num = len(existing_ids) + 1
        new_id = f"FIELD-{chr(65+field_num-1)}{field_num:02d}"
        
        field_data['id'] = new_id
        field_data['created'] = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
        field_data['progress'] = 0
        field_data['status'] = '待作业'
        field_data['paths'] = []
        
        data['fields'].append(field_data)
        
        with open('maps/fields.json', 'w', encoding='utf-8') as f:
            json.dump(data, f, ensure_ascii=False, indent=2)
        
        logger.info(f"[GIS] 新建地块: {new_id} - {field_data['name']}")
        return jsonify({'success': True, 'field_id': new_id, 'field': field_data})
    except Exception as e:
        logger.error(f"[GIS] 新建地块失败: {e}")
        return jsonify({'success': False, 'error': str(e)})

@app.route('/api/gis/field/<field_id>/update', methods=['POST'])
def api_update_field(field_id):
    """修改地块信息"""
    try:
        from flask import request
        updates = request.json
        
        with open('maps/fields.json', 'r', encoding='utf-8') as f:
            data = json.load(f)
        
        for field in data['fields']:
            if field['id'] == field_id:
                # 更新允许修改的字段
                if 'name' in updates:
                    field['name'] = updates['name']
                if 'area' in updates:
                    field['area'] = updates['area']
                if 'center' in updates:
                    field['center'] = updates['center']
                if 'boundary' in updates:
                    field['boundary'] = updates['boundary']
                if 'crop_type' in updates:
                    field['crop_type'] = updates['crop_type']
                if 'planting_date' in updates:
                    field['planting_date'] = updates['planting_date']
                if 'paths' in updates:
                    field['paths'] = updates['paths']
                break
        
        with open('maps/fields.json', 'w', encoding='utf-8') as f:
            json.dump(data, f, ensure_ascii=False, indent=2)
        
        logger.info(f"[GIS] 更新地块: {field_id}")
        return jsonify({'success': True})
    except Exception as e:
        logger.error(f"[GIS] 更新地块失败: {e}")
        return jsonify({'success': False, 'error': str(e)})

@app.route('/api/gis/map/<map_id>/save_path', methods=['POST'])
def api_save_map_path(map_id):
    """保存路径到指定地图"""
    try:
        from flask import request
        path_data = request.json
        path_id = path_data.get('id')
        path_name = path_data.get('name', '')
        
        logger.info(f"[GIS-SAVE] ========== 收到保存请求 ==========")
        logger.info(f"[GIS-SAVE] 地图ID: {map_id}")
        logger.info(f"[GIS-SAVE] 路径ID: {path_id}")
        logger.info(f"[GIS-SAVE] 路径名: {path_name}")
        
        if not path_id:
            logger.error(f"[GIS-SAVE] 路径ID为空，拒绝保存")
            return jsonify({'success': False, 'error': '路径ID不能为空'})
        
        filename = f'maps/{map_id}_paths.json'
        
        # 读取现有数据
        try:
            with open(filename, 'r', encoding='utf-8') as f:
                data = json.load(f)
        except:
            data = {'map_id': map_id, 'map_name': '', 'paths': []}
        
        # 检查路径ID是否已存在（防止重复保存）
        existing_ids = [p['id'] for p in data['paths']]
        logger.info(f"[GIS-SAVE] 当前已有路径数: {len(data['paths'])}")
        logger.info(f"[GIS-SAVE] 现有路径IDs: {existing_ids}")
        
        if path_id in existing_ids:
            logger.warning(f"[GIS-SAVE] 路径ID已存在，拒绝重复保存: {path_id}")
            return jsonify({'success': True, 'path_id': path_id, 'map_id': map_id, 'duplicated': True})
        
        # 添加新路径
        data['paths'].append(path_data)
        logger.info(f"[GIS-SAVE] 添加新路径，当前总数: {len(data['paths'])}")
        
        # 保存路径文件
        with open(filename, 'w', encoding='utf-8') as f:
            json.dump(data, f, ensure_ascii=False, indent=2)
        logger.info(f"[GIS-SAVE] 文件已保存: {filename}")
        
        # 自动关联地块（根据路径中心点）
        if 'center' in path_data and path_data['center']:
            auto_associate_field(path_data['id'], path_data['center'])
        
        logger.info(f"[GIS-SAVE] 保存成功: {path_id} - {path_name}")
        logger.info(f"[GIS-SAVE] ========== 保存请求完成 ==========")
        return jsonify({'success': True, 'path_id': path_data['id'], 'map_id': map_id})
    except Exception as e:
        logger.error(f"[GIS-SAVE] 保存路径失败: {e}")
        import traceback
        logger.error(traceback.format_exc())
        return jsonify({'success': False, 'error': str(e)})

def auto_associate_field(path_id, path_center):
    """自动关联路径到最近的地块"""
    try:
        with open('maps/fields.json', 'r', encoding='utf-8') as f:
            data = json.load(f)
        
        # 计算距离并找到最近的地块
        min_distance = float('inf')
        nearest_field = None
        
        for field in data['fields']:
            if not field.get('center'):
                continue
            
            # 简单距离计算
            dlat = field['center'][0] - path_center[0]
            dlng = field['center'][1] - path_center[1]
            distance = dlat*dlat + dlng*dlng
            
            if distance < min_distance:
                min_distance = distance
                nearest_field = field
        
        # 关联到最近的地块
        if nearest_field and min_distance < 0.01:  # 距离阈值
            if 'paths' not in nearest_field:
                nearest_field['paths'] = []
            
            if path_id not in nearest_field['paths']:
                nearest_field['paths'].append(path_id)
                
                # 保存更新
                with open('maps/fields.json', 'w', encoding='utf-8') as f:
                    json.dump(data, f, ensure_ascii=False, indent=2)
                
                logger.info(f"[GIS] 路径 {path_id} 自动关联到地块 {nearest_field['id']}")
    except Exception as e:
        logger.error(f"[GIS] 自动关联失败: {e}")

@app.route('/api/gis/save_path', methods=['POST'])
def api_save_path():
    """保存新路径（兼容旧接口）"""
    try:
        from flask import request
        path_data = request.json
        map_id = path_data.get('map_id', 'MAP-001')
        return api_save_map_path(map_id)
    except Exception as e:
        logger.error(f"[GIS] 保存路径失败: {e}")
        return jsonify({'success': False, 'error': str(e)})

@app.route('/api/gis/map/<map_id>/delete_path/<path_id>', methods=['DELETE'])
def api_delete_map_path(map_id, path_id):
    """从指定地图删除路径"""
    try:
        filename = f'maps/{map_id}_paths.json'
        
        with open(filename, 'r', encoding='utf-8') as f:
            data = json.load(f)
        
        data['paths'] = [p for p in data['paths'] if p['id'] != path_id]
        
        with open(filename, 'w', encoding='utf-8') as f:
            json.dump(data, f, ensure_ascii=False, indent=2)
        
        logger.info(f"[GIS] 从{map_id}删除路径: {path_id}")
        return jsonify({'success': True})
    except Exception as e:
        logger.error(f"[GIS] 删除路径失败: {e}")
        return jsonify({'success': False, 'error': str(e)})

# ========== WebSocket事件 ==========

@socketio.on('connect')
def handle_connect(auth=None):
    logger.info("[Web] 客户端连接")

@socketio.on('movement_control')
def handle_movement(data):
    forward = data.get('forward', 0)
    turn = data.get('turn', 0)
    logger.info(f"[Web->Server] 运动控制: 前进={forward}, 转向={turn}, 目标机器人={current_robot}")
    
    result = send_to_robot(current_robot, {
        'type': 'movement',
        'forward': forward,
        'turn': turn
    })
    
    if not result:
        logger.warning(f"[Web->Server] 发送失败！机器人{current_robot}可能未连接")
        emit('control_failed', {'message': '机器人未连接，无法发送运动控制命令'})

@socketio.on('control_command')
def handle_control(data):
    send_to_robot(current_robot, {
        'type': 'control',
        'control_type': data.get('type'),
        'value': data.get('value', 0)
    })

@socketio.on('continuous_forward_test')
def handle_test(data):
    send_to_robot(current_robot, {
        'type': 'continuous_forward',
        'duration': data.get('duration', 5000)
    })

@socketio.on('assign_task')
def handle_assign_task(data):
    """处理任务分配"""
    robot_id = data.get('robot_id')
    path_id = data.get('path_id')
    path_name = data.get('path_name', '')
    path_points = data.get('path_points', [])
    mode = data.get('mode', 'auto')
    field_id = data.get('field_id')
    work_params = data.get('work_params')
    
    logger.info(f"[任务分配] 机器人: {robot_id}, 路径: {path_name} ({path_id}), 模式: {mode}")
    logger.info(f"[任务分配] 路径点数: {len(path_points)}")
    
    # 创建数据库任务记录
    task_id = f"TASK-{int(time.time() * 1000)}"
    db.create_task(task_id, robot_id, path_id, field_id=field_id, 
                   task_type=mode, work_params=work_params)
    
    # 发送任务给机器人
    task_command = {
        'type': 'path_task',
        'task_id': task_id,
        'path_id': path_id,
        'path_name': path_name,
        'path_points': path_points,
        'mode': mode
    }
    
    success = send_to_robot(robot_id, task_command)
    
    if success:
        logger.info(f"[任务分配] 任务已发送给 {robot_id}")
        db.update_task_status(task_id, 'sent')
        emit('task_assigned', {'success': True, 'robot_id': robot_id, 'path_name': path_name, 'task_id': task_id})
    else:
        logger.error(f"[任务分配] 发送失败，机器人 {robot_id} 未连接")
        emit('task_assigned', {'success': False, 'error': '机器人未连接'})

@socketio.on('plc_control')
def handle_plc_control(data):
    """处理PLC控制命令"""
    robot_id = data.get('robot_id')
    control_type = data.get('control_type')
    value = data.get('value', 0)
    
    # PLC寄存器地址映射
    plc_register_map = {
        'control_authority': 120,  # D120
        'speed_percent': 127,      # D127
        'generator_start': 123,    # D123
        'relay_220v': 124,         # D124
        'water_spray': 125,        # D125
        'cooling_control': 126,    # D126
        'blade_control': 128       # D128 (割叶机)
    }
    
    register_addr = plc_register_map.get(control_type)
    
    if not register_addr:
        logger.error(f"[PLC控制] 未知的控制类型: {control_type}")
        emit('plc_control_result', {'success': False, 'error': '未知的控制类型'})
        return
    
    logger.info(f"[PLC控制] 机器人: {robot_id}, 类型: {control_type} (D{register_addr}), 值: {value}")
    
    # 发送控制命令到机器人
    control_command = {
        'type': 'control',
        'control_type': control_type,
        'register': register_addr,
        'value': value
    }
    
    success = send_to_robot(robot_id, control_command)
    
    if success:
        logger.info(f"[PLC控制] 命令已发送给 {robot_id}")
        emit('plc_control_result', {'success': True, 'robot_id': robot_id, 'control_type': control_type, 'value': value})
    else:
        logger.error(f"[PLC控制] 发送失败，机器人 {robot_id} 未连接")
        emit('plc_control_result', {'success': False, 'error': '机器人未连接'})

@socketio.on('request_sensor_data')
def handle_request_sensor_data(data):
    """请求读取PLC传感器数据"""
    robot_id = data.get('robot_id', 'GZJY001')
    
    logger.info(f"[传感器] 请求读取 {robot_id} 的传感器数据")
    
    command = {
        'type': 'read_sensors'
    }
    
    success = send_to_robot(robot_id, command)
    
    if not success:
        logger.error(f"[传感器] 发送失败，机器人 {robot_id} 未连接")
        emit('sensor_data_result', {'success': False, 'error': '机器人未连接', 'robot_id': robot_id})

# ==================== 数据库API端点 ====================

@app.route('/api/work/start', methods=['POST'])
def api_work_start():
    """开始作业"""
    data = request.json
    robot_id = data.get('robot_id', current_robot)
    field_id = data.get('field_id')
    path_id = data.get('path_id')
    
    # 获取当前GPS位置
    robot_data = get_robot(robot_id)
    gps = robot_data.get('gps', {})
    lat = gps.get('latitude')
    lon = gps.get('longitude')
    
    # 开始作业会话
    session_id = db.start_work_session(robot_id, field_id, path_id, lat, lon)
    
    # 保存到机器人状态
    robots[robot_id]['active_session'] = session_id
    
    logger.info(f"[作业开始] 机器人:{robot_id}, 会话ID:{session_id}")
    
    return jsonify({'success': True, 'session_id': session_id})

@app.route('/api/work/stop', methods=['POST'])
def api_work_stop():
    """停止作业 - 自动计算统计数据"""
    data = request.json
    robot_id = data.get('robot_id', current_robot)
    
    # 获取活跃会话
    session_id = robots.get(robot_id, {}).get('active_session')
    
    if session_id:
        # 获取当前GPS位置
        robot_data = get_robot(robot_id)
        gps = robot_data.get('gps', {})
        lat = gps.get('lat')
        lon = gps.get('lng')
        
        # 获取GPS轨迹
        gps_track = db.get_gps_track(session_id)
        gps_points = [(point['latitude'], point['longitude']) for point in gps_track]
        
        # 获取会话信息（包括field_id）
        session_info = db.get_active_session(robot_id)
        field_id = session_info.get('field_id') if session_info else None
        
        # 获取地块边界（如果有）
        field_boundary = None
        if field_id:
            try:
                import os
                fields_path = os.path.join('maps', 'fields.json')
                if os.path.exists(fields_path):
                    with open(fields_path, 'r', encoding='utf-8') as f:
                        fields_data = json.load(f)
                        for field in fields_data.get('fields', []):
                            if field.get('id') == field_id:
                                field_boundary = field.get('boundary', [])
                                break
            except:
                pass
        
        # 计算统计数据
        # 计算时长（如果有开始时间）
        duration_seconds = 0
        if session_info and session_info.get('start_time'):
            try:
                start_time = datetime.fromisoformat(session_info['start_time'])
                duration_seconds = int((datetime.now() - start_time).total_seconds())
            except:
                duration_seconds = 0
        
        stats = calculator.analyze_work_session(
            gps_points,
            field_boundary,
            work_width=2.0,  # 可配置的作业宽度
            duration_seconds=duration_seconds,
            avg_speed=5.0  # 平均速度
        )
        
        # 使用计算出的数据
        area_covered = stats['work_area_mu']
        distance = stats['distance_m']
        fuel = stats['fuel_consumption_l']
        notes = data.get('notes', f"作业效率: {stats['efficiency_mu_per_hour']}亩/小时")
        
        db.end_work_session(session_id, area_covered, distance, fuel, lat, lon, notes)
        
        # 清除活跃会话
        robots[robot_id]['active_session'] = None
        
        logger.info(f"[作业停止] 机器人:{robot_id}, 会话ID:{session_id}, 面积:{area_covered:.2f}亩, 距离:{distance:.0f}米")
        
        return jsonify({
            'success': True, 
            'session_id': session_id,
            'stats': stats
        })
    else:
        return jsonify({'success': False, 'error': '没有活跃的作业会话'})

@app.route('/api/work/history')
def api_work_history():
    """获取作业历史"""
    robot_id = request.args.get('robot_id')
    limit = int(request.args.get('limit', 50))
    status = request.args.get('status')
    
    history = db.get_work_history(robot_id, limit, status)
    return jsonify({'success': True, 'data': history})

@app.route('/api/work/active')
def api_work_active():
    """获取活跃作业"""
    robot_id = request.args.get('robot_id', current_robot)
    session = db.get_active_session(robot_id)
    
    return jsonify({'success': True, 'data': session})

@app.route('/api/tasks')
def api_tasks():
    """获取任务列表"""
    robot_id = request.args.get('robot_id')
    status = request.args.get('status')
    limit = int(request.args.get('limit', 50))
    
    tasks = db.get_tasks(robot_id, status, limit)
    return jsonify({'success': True, 'data': tasks})

@app.route('/api/stats/daily')
def api_stats_daily():
    """获取每日统计"""
    date_str = request.args.get('date')
    from datetime import datetime
    date = datetime.strptime(date_str, '%Y-%m-%d').date() if date_str else None
    
    stats = db.get_daily_stats(date)
    return jsonify({'success': True, 'data': stats})

@app.route('/api/stats/weekly')
def api_stats_weekly():
    """获取每周统计"""
    weeks = int(request.args.get('weeks', 4))
    stats = db.get_weekly_stats(weeks)
    return jsonify({'success': True, 'data': stats})

@app.route('/api/stats/robot')
def api_stats_robot():
    """获取机器人统计"""
    robot_id = request.args.get('robot_id', current_robot)
    days = int(request.args.get('days', 30))
    
    stats = db.get_robot_stats(robot_id, days)
    return jsonify({'success': True, 'data': stats})

@app.route('/api/events')
def api_events():
    """获取事件日志"""
    robot_id = request.args.get('robot_id')
    level = request.args.get('level')
    limit = int(request.args.get('limit', 100))
    
    events = db.get_event_logs(robot_id, level, limit)
    return jsonify({'success': True, 'data': events})

# ==================== 报表生成API ====================

@app.route('/api/report/generate/<int:session_id>')
def api_generate_report(session_id):
    """生成作业报告"""
    output_format = request.args.get('format', 'html')  # html 或 json
    
    # 获取会话数据
    history = db.get_work_history(limit=1000)
    session_data = None
    for session in history:
        if session['id'] == session_id:
            session_data = session
            break
    
    if not session_data:
        return jsonify({'success': False, 'error': '会话不存在'}), 404
    
    # 获取GPS轨迹
    gps_track = db.get_gps_track(session_id)
    
    # 生成报告
    try:
        report_content = report_gen.generate_work_session_report(
            session_data, 
            gps_track, 
            output_format
        )
        
        # 保存报告
        timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
        ext = 'html' if output_format == 'html' else 'json'
        filename = f"work_report_{session_id}_{timestamp}.{ext}"
        
        filepath = report_gen.save_report(report_content, filename)
        
        logger.info(f"[报表生成] 会话ID:{session_id}, 格式:{output_format}, 文件:{filename}")
        
        return jsonify({
            'success': True,
            'filename': filename,
            'filepath': filepath,
            'download_url': f'/api/report/download/{filename}'
        })
    
    except Exception as e:
        logger.error(f"[报表生成] 错误: {e}")
        return jsonify({'success': False, 'error': str(e)}), 500

@app.route('/api/report/download/<filename>')
def api_download_report(filename):
    """下载报告文件"""
    import os
    filepath = os.path.join('reports', filename)
    
    if not os.path.exists(filepath):
        return jsonify({'success': False, 'error': '文件不存在'}), 404
    
    return send_file(filepath, as_attachment=True)

@app.route('/api/report/list')
def api_list_reports():
    """列出所有报告"""
    import os
    reports_dir = 'reports'
    
    if not os.path.exists(reports_dir):
        return jsonify({'success': True, 'data': []})
    
    files = []
    for filename in os.listdir(reports_dir):
        filepath = os.path.join(reports_dir, filename)
        stat = os.stat(filepath)
        files.append({
            'filename': filename,
            'size': stat.st_size,
            'created': datetime.fromtimestamp(stat.st_ctime).isoformat(),
            'download_url': f'/api/report/download/{filename}'
        })
    
    files.sort(key=lambda x: x['created'], reverse=True)
    return jsonify({'success': True, 'data': files})

if __name__ == '__main__':
    logger.info("=" * 60)
    logger.info("  甘蔗农机智能管控平台 - Windows服务器")
    logger.info("=" * 60)
    logger.info("")
    logger.info("   网络模式: WiFi局域网")
    logger.info("")
    logger.info("   连接配置:")
    logger.info("     - Windows IP: 192.168.250.187")
    logger.info("     - RK3588 IP: 192.168.250.110")
    logger.info("     - 访问地址: http://localhost:5000")
    logger.info("")
    logger.info("   RK3588设备:")
    logger.info("     - 视频流: http://192.168.250.110:8080 / 8081")
    logger.info("     - 数据端口: 9000-9003")
    logger.info("")
    logger.info(f"    心跳超时: {HEARTBEAT_TIMEOUT}秒")
    logger.info("")
    logger.info("=" * 60)
    
    # 启动TCP接收器
    start_control_receiver()
    start_lidar_receiver()
    start_gps_receiver()
    start_plc_receiver()
    
    # 启动心跳超时检测线程
    threading.Thread(target=check_robot_timeout, daemon=True, name='HeartbeatChecker').start()
    logger.info("[心跳检测] 线程已启动")
    
    socketio.run(app, host='0.0.0.0', port=5000, debug=False)

